package pkg

import (
	"context"
	"encoding/json"
	"fmt"
	"sale-admin/config"
	"sale-admin/config/redis"
	"sale-admin/internal/define"
	"strconv"
	"strings"
	"time"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"github.com/cristalhq/jwt/v5"

	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
)

type StripeEx struct{}

const (
	BaseURL  = "https://sg-pay.iybot.com"
	Email    = "dadao@production"        //  dadao@localhost" 测试环境
	Password = "bd3aad331808ca10be5293e" //  NQyzaxGxsso" 测试环境
)
const (
	TokenKey  = define.RedisApp + "stripe_ex:token"
	DeviceKey = define.RedisApp + "stripe_ex:device"

	// dev环境 设备id = 5, 对应签名秘钥 QbfgYzA1PVagSD1oTqrvFixwYGpprPsuxrlh1mSeiTos869TeRduQ3Qa0Fp3
)

type TokenStruct struct {
	Token string `json:"token"`
}

func (StripeEx) CronTokenCheck() {
	StripeEx{}.GetToken(context.Background())
}

// 创建token
func (StripeEx) GetToken(ctx context.Context) (token string) {
	rdb := redis.NewDB()
	TokenInfo, err := rdb.Get(ctx, TokenKey).Result()
	if err == nil && TokenInfo != "" {
		return TokenInfo
	}
	QueryURL := BaseURL + "/api/v1/authentications"

	var PostData struct {
		User struct {
			Email    string `json:"email"`
			Password string `json:"password"`
		} `json:"user"`
	}
	PostData.User.Email = Email
	PostData.User.Password = Password
	header := map[string]string{
		"Content-Type": "application/json",
	}
	statusCode, bytes, err := helper.Net{}.Request(ctx, "POST", QueryURL, header, PostData)
	res := TokenStruct{}
	json.Unmarshal(bytes, &res)
	logx.Info(ctx, "stripe_ex get token",
		logx.Any("queryURL", QueryURL),
		logx.Any("statusCode", statusCode),
		logx.Any("bytes", string(bytes)),
		logx.Any("err", err))
	if statusCode == 200 && err == nil {
		token, _ := jwt.ParseNoVerify([]byte(res.Token))
		tokenMsg := struct {
			Data interface{} `json:"data"`
			Exp  int64       `json:"exp"`
		}{}
		json.Unmarshal(token.Claims(), &tokenMsg)
		exp := time.Duration((tokenMsg.Exp - time.Now().Unix() - 3600)) * time.Second
		rdb.Set(ctx, TokenKey, res.Token, time.Duration(exp))
	}
	return res.Token
}

type DeviceInfoStruct struct {
	SmartDevice struct {
		SmartDeviceID int    `json:"smart_device_id"`
		Name          string `json:"name"` // 大于8位，设备名称
		Description   struct {
			UUID string `json:"uuid"`
		} `json:"description"` // 可选项 json对象 设备描述
		CreatedAt int `json:"-"`
		UpdatedAt int `json:"-"`
	} `json:"smart_device"`
	Webhook struct {
		Endpoint  string `json:"endpoint"`  // 必须符合URL的格式。这是设备的webhook地址。
		Signature string `json:"signature"` // 这是设备的webhook签名密钥,⽤来验证webhook的合法性
		Enabled   bool   `json:"enabled"`   // 默认为 false ,这是设备的webhook开关,关闭的状态下不会收到Payment状态变更的Webhook,需要⼿动查询Payment的状态。
	} `json:"webhook"`
}

// 创建设备
func (StripeEx) GetDeviceInfo(ctx context.Context) (deviceID int, signature string) {
	rdb := redis.NewDB()
	DeviceInfo, err := rdb.Get(ctx, DeviceKey).Result()
	if err == nil && DeviceInfo != "" {
		device := strings.Split(DeviceInfo, "&")
		deviceID, _ = strconv.Atoi(device[0])
		return deviceID, device[1]
	}
	Signature := helper.RandString(60)
	QueryURL := BaseURL + "/api/v1/smart_devices"

	var PostData DeviceInfoStruct
	PostData.SmartDevice.Name = "DDRetail" + strconv.FormatInt(time.Now().Unix(), 10)
	PostData.SmartDevice.Description.UUID = PostData.SmartDevice.Name
	PostData.Webhook.Endpoint = config.Config.BaseUrl + "/admin/pay/stripe-ex-webhook"
	PostData.Webhook.Signature = Signature
	PostData.Webhook.Enabled = true

	token := StripeEx{}.GetToken(ctx)

	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"
	headers["Authorization"] = "Bearer " + token

	statusCode, bytes, err := helper.Net{}.Request(ctx, "POST", QueryURL, headers, PostData)
	res := DeviceInfoStruct{}
	json.Unmarshal(bytes, &res)

	logx.Info(ctx, "stripe_ex smart_devices",
		logx.Any("queryURL", QueryURL),
		logx.Any("statusCode", statusCode),
		logx.Any("bytes", string(bytes)),
		logx.Any("err", err))
	rdb.Set(ctx, DeviceKey, strconv.Itoa(res.SmartDevice.SmartDeviceID)+"&"+Signature, 0)
	return res.SmartDevice.SmartDeviceID, Signature
}

type PayRsp struct {
	CheckoutURL string `json:"checkout_url"` // 这是⼀个有效的URL，⽤户可以通过这个URL进⾏⽀付。这个URL的有效时间为24⼩时，过期后将⽆法访问
	PaymentID   string `json:"payment_id"`   // 这是确认⽀付状态的webhook标识，⽤于在⽀付完成后通知服务器⽀付的结果
	Amount      int    `json:"amount"`       // 支付金额
	Currency    string `json:"currency"`     // 支付的货币类型
	Status      string `json:"status"`       // 支付状态 可选[pending , succeeded , failed , refunded]
	RedirectURL string `json:"redirect_url"` // 支付成功重定向
	UpdatedAt   int    `json:"updated_at"`   // 支付的更新时间戳
	CreatedAt   int    `json:"created_at"`   // 支付的创建时间戳
	SmartDevice struct {
		SmartDevice struct { // 支付的设备信息
			SmartDeviceID int      `json:"smart_device_id"` // 支付设备id
			Name          string   `json:"name"`            // 支付设备名称
			Description   struct { // json对象 支付设备描述
				UUID string `json:"uuid"`
			} `json:"description"`
		} `json:"smart_device"`
		Webhook struct { // 支付设备的webhook地址
			Endpoint  string `json:"endpoint"`
			Signature string `json:"signature"`
			Enabled   string `json:"enabled"`
		} `json:"webhook"`
	} `json:"smart_device"`
}

// 创建支付
func (StripeEx) Pay(ctx context.Context, amount int64) (data PayRsp, err error) {
	stripeDeviceID, _ := StripeEx{}.GetDeviceInfo(ctx)
	QueryURL := BaseURL + "/api/v1/payments"
	PostData := make(map[string]interface{})
	PostData["payment"] = map[string]interface{}{
		"amount":          amount,
		"redirect_url":    config.Config.BaseUrl + "/admin/pay/stripe-ex-success", // 支付成功重定向地址
		"smart_device_id": stripeDeviceID,                                         // 支付的设备id
		"currency":        "sgd",                                                  // 币种 默认cny,可选 cny和 sgd
		"without_card":    true,                                                   // 默认false,设置为true时，没有银行卡付款信息，不支持apple pay 和 google pay
	}

	token := StripeEx{}.GetToken(ctx)
	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"
	headers["Authorization"] = "Bearer " + token

	// Amount must be at least $0.50 sgd
	statusCode, bytes, err := helper.Net{}.Request(ctx, "POST", QueryURL, headers, PostData)
	res := PayRsp{}
	json.Unmarshal(bytes, &res)
	logx.Info(ctx, "stripe_ex Pay",
		logx.Any("queryURL", QueryURL),
		logx.Any("statusCode", statusCode),
		logx.Any("post_data", PostData),
		logx.Any("headers", headers),
		logx.Any("bytes", string(bytes)))
	if err != nil {
		logx.Error(ctx, "stripe_ex pay error", logx.Any("error", err.Error()))
		err = errorx.New("支付错误")
	}
	return res, err
}

// 获取payment 订单状态查询
func (StripeEx) GetPayment(ctx context.Context, paymentID string) (res PayRsp, err error) {
	QueryURL := fmt.Sprintf(BaseURL+"/api/v1/payments/%s", paymentID)

	token := StripeEx{}.GetToken(ctx)
	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"
	headers["Authorization"] = "Bearer " + token

	statusCode, bytes, err := helper.Net{}.Request(ctx, "GET", QueryURL, headers, map[string]interface{}{})
	json.Unmarshal(bytes, &res)
	logx.Info(ctx, "stripe_ex GetPayment",
		logx.Any("queryURL", QueryURL),
		logx.Any("statusCode", statusCode),
		logx.Any("bytes", string(bytes)),
		logx.Any("err", err))
	if statusCode != 200 {
		return res, errorx.New("stripe支付失败", -1)
	}
	return res, err
}

type RefundRsp struct {
	Status string `json:"status"`
}

// 退款
// 创建成功并不⼀定退款成功,退款状态需要通过webhook payment_intent.refunded 来确认payment的退款状态
func (StripeEx) Refund(ctx context.Context, paymentID, reason string) {
	QueryURL := fmt.Sprintf(BaseURL+"/api/v1/payments/%s/refund", paymentID)

	PostData := make(map[string]interface{})
	PostData["refund"] = map[string]interface{}{
		"reason": "requested_by_customer",
	}

	token := StripeEx{}.GetToken(ctx)
	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"
	headers["Authorization"] = "Bearer " + token

	statusCode, bytes, err := helper.Net{}.Request(ctx, "POST", QueryURL, headers, PostData)
	res := RefundRsp{}
	json.Unmarshal(bytes, &res)
	logx.Info(ctx, "stripe_ex Refund",
		logx.Any("queryURL", QueryURL),
		logx.Any("statusCode", statusCode),
		logx.Any("bytes", string(bytes)),
		logx.Any("err", err))
}

// 获取签名
func (StripeEx) GetSignature(message []byte) (sign string) {
	_, SecretKey := StripeEx{}.GetDeviceInfo(context.Background())
	// 创建一个 HMAC SHA256 的哈希对象
	hash := hmac.New(sha256.New, []byte(SecretKey))
	// 写入要签名的消息
	hash.Write(message)
	// 计算签名结果
	signature := hash.Sum(nil)
	// 将签名结果转换为十六进制字符串
	signatureHex := hex.EncodeToString(signature)
	return signatureHex
}
